【巡检问题分析与最佳实践】Redis内存高问题

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
简介: 作为内存型数据库,阿里云数据库Redis的内存使用率是一个非常重要的监控指标,当实例内存不足时会导致数据库响应缓慢,甚至导致写入错误等不利影响。

往期分享

RDS MySQL

RDS MySQL 实例空间问题

RDS MySQL 内存使用问题

RDS MySQL 活跃线程数高问题

RDS MySQL 慢SQL问题

RDS MySQL 实例IO高问题

RDS MySQL 小版本升级最佳实践

RDS PostgreSQL

RDS PostgreSQL 实例IO高问题

RDS PostgreSQL 慢SQL问题

RDS PostgreSQL CPU高问题

RDS SQL Server

RDS SQL Server 磁盘IO吞吐高问题

RDS SQL Server CPU高问题

RDS SQL Server 空间使用问题

Redis

Redis 流控问题

概述

作为内存型数据库,阿里云数据库Redis的内存使用率是一个非常重要的监控指标,当实例内存不足时会导致数据库响应缓慢,甚至导致写入错误等不利影响。

云数据库Redis具有内置保护功能,用户申请规格中的内存与配置文件中的maxmemory保持一致,即该实例可以使用的内存上限。如果达到此限制,Redis将开始以写命令的错误答复(但将继续接受只读命令),或者您可以将其配置为在达到最大内存限制时采用key逐出的方式以保证Redis的正常运行。

查看内存使用

部署架构为主备模式下,只提供当前主节点的内存使用。通过监控图查看内存的总体使用率非常方便,点击控制台"性能监控"后一目了然。

1.png

部署架构为Redis集群/读写分离模式下,由于多个物理节点组成了一个逻辑实例,且可能通过多个Proxy节点做路由转发和负载均衡,通过监控图查看内存时需要注意以下几点

  • 数据节点聚合指标:在集群模式下,Used Memory显示值为“该集群下所有子Shard的内存使用总和”,这里的子Shard内存不包括备用物理节点和只读实例;在读写分离模式下,Used Memory显示值为“该读写分离实例下各个物理节点内存的平均值”,基本上与单个物理节点内存使用量相同
  • 数据节点:单个Shard节点的内存使用与主备模式相同,但需要人为选择需要查看的物理节点。
  • Proxy节点和Proxy节点聚合:由于Proxy节点只做请求转发和请求的负载均衡,不会实际存储数据,所以基本上不会存在内存瓶颈,所以不提供Proxy组件的内存使用。

2.png

内存使用详细分析

   通过阿里云控制台提供的监控图可以大概确认当前实例的内存使用情况,在大部分场景下,绝大部分的内存均为实际的数据存储需求,当观察到内存超过一定阈值后扩容即可。然而在一些特定场景下,我们可能认为Redis的内存使用不符合预期,这时就需要对Redis实例的内存使用做详细的分析了。

   Redis官方提供有info memory和memory模块做更为精细的内存分析。相比较而言,memory模块的内存详情分析更为细致,本文以memory模块的结果解读作为Redis内存使用详情分析。memory模块一共提供有5个命令,通过memory help指令可以查看各命令得使用方式和概述,我们重点讲解memory stats命令。

r-bp17so24bq3wdor5i0.redis.rds.aliyuncs.com:6379> memory help
1) "MEMORY DOCTOR                        - Outputs memory problems report"
2) "MEMORY USAGE <key> [SAMPLES <count>] - Estimate memory usage of key"
3) "MEMORY STATS                         - Show memory usage details"
4) "MEMORY PURGE                         - Ask the allocator to release memory"
5) "MEMORY MALLOC-STATS                  - Show allocator internal stats"

memory stats

   MEMORY STATS命令返回有关服务器内存使用情况详情,主要报告了以下指标内容,解读参考注释部分

r-bp17so24bq3wdor5i0.redis.rds.aliyuncs.com:6379> memory stats
 1) "peak.allocated" //Redis进程启动以来消耗的峰值内存(以字节为单位)
 2) (integer) 79492312
 3) "total.allocated" //Redis使用其分配器分配的总字节数,即当前的总内存使用量
 4) (integer) 79307776
 5) "startup.allocated" //Redis启动时消耗的初始内存量(以字节为单位)
 6) (integer) 45582592
 7) "replication.backlog" //复制积压缓冲区的大小(以字节为单位),在主备复制断开重连时使用
 8) (integer) 33554432
 9) "clients.slaves" //主从复制中所有slave的读写缓冲区大小(以字节为单位)
10) (integer) 17266
11) "clients.normal" //除slave外所有其他客户端的读写缓冲区(以字节为单位)
12) (integer) 119102
13) "aof.buffer" //aof持久化使用的缓存和aofrewrite时产生的缓存
14) (integer) 0
15) "db.0"  //有多少个业务db,这里就会展示多少个
16) 1) "overhead.hashtable.main" //当前db的hash链表开销内存总和,可以认为是元数据内存
    2) (integer) 144
    3) "overhead.hashtable.expires" //当前db存储“设置了key的expire时间”消耗的内存
    4) (integer) 0
    5) "overhead.hashtable.inmem_keys"
    6) (integer) 80
17) "overhead.total" //数值=startup.allocated+replication.backlog+clients.slaves+clients.normal+aof.buffer+db.X
18) (integer) 79273616
19) "keys.count" //当前Redis实例的key总数
20) (integer) 2
21) "keys.bytes-per-key" //当前Redis实例每个key的平均大小,数值=(total.allocated-startup.allocated)/keys.count
22) (integer) 16862592
23) "dataset.bytes" //纯业务数据占用的内存大小
24) (integer) 34160
25) "dataset.percentage" //纯业务数据占用的内存比例,数值=dataset.bytes*100/(total.allocated-startup.allocated)
26) "0.1012892946600914"
27) "peak.percentage" //当前总内存与历史峰值的比例,数值=total.allocated*100/peak.allocated
28) "99.767860412597656"
29) "fragmentation" //内存的碎片率
30) "0.45836541056632996"

从上述的指标解读可以看出,正常情况下消耗一个Redis实例内存的绝大部分是纯业务数据,但是也有一些其他方面的内存开销,比如主备复制的积压缓冲区,Redis进程初始化消耗的内存,Redis内部维护Key-Value链表消耗的内存等等。当这些非dataset消耗的内存不多时,我们建议您直接忽略,重点分析dataset的内存使用情况是否符合预期;如果非dataset消耗的内存也占据了相当大的比例,需要根据上述指标解读一一查看内存消耗的根因。

更多memory stats的内存请参考

https://redis.io/commands/memory-stats

memory doctor

   memory docto命令可以提供当前Redis实例关于内存使用方面的建议,在不同的允许状态下会有不同的分析结果,仅供用户参考。

r-bp17so24bq3wdor5i0.redis.rds.aliyuncs.com:6379> memory doctor
Hi Sam, I can't find any memory issue in your instance. I can only account for what occurs on this base

除了上述范例以外,memory doctor还会从其他多个维度给出当前Redis实例的内存诊断建议,主要包括以下方面,您可以根据指令结果制定相应的优化策略。

    int empty = 0;          /* Instance is empty or almost empty. */
    int big_peak = 0;       /* Memory peak is much larger than used mem. */
    int high_frag = 0;      /* High fragmentation. */
    int high_alloc_frag = 0;/* High allocator fragmentation. */
    int high_proc_rss = 0;  /* High process rss overhead. */
    int high_alloc_rss = 0; /* High rss overhead. */
    int big_slave_buf = 0;  /* Slave buffers are too big. */
    int big_client_buf = 0; /* Client buffers are too big. */
    int many_scripts = 0;   /* Script cache has too many scripts. */

memory usage

   MEMORY USAGE命令展示了指定Key消耗的内存,单位为Byte,表示在Redis中存储该Key所需的用于数据和管理开销的内存分配的总和。

   对于嵌套数据类型,提供可选的SAMPLES选项,其中count是采样的嵌套值的数量。默认情况下,此选项设置为5。要对所有嵌套值进行采样,使用SAMPLES 0。

r-bp17so24bq3wdor5i0.redis.rds.aliyuncs.com:6379> memory usage hudao
(integer) 252
r-bp17so24bq3wdor5i0.redis.rds.aliyuncs.com:6379> memory usage hudao_not_exit
(nil)

更多关于memory usage的内容参考

https://redis.io/commands/memory-usage

内存数据逐出和expire策略

   前文提到,当云数据库Redis内存不足时,如果用户配置了对应的数据逐出策略,则数据会做自动淘汰以保证Redis实例能够正常运行。在阿里云数据库Redis实例的默认逐出策略是volatile-lru, 如需修改,可以登录控制台在系统参数中修改。

3.png

阿里云Redis支持的数据逐出策略

  • volatile-lru
    按照LRU算法逐出原有数据,但仅逐出设置了过期时间的数据。
  • volatile-ttl
    仅逐出设置了过期时间的数据,并且是按照TTL由小到大的顺序进行逐出。
  • allkeys-lru
    按照LRU算法逐出原有数据。
  • volatile-random
    随机逐出原有数据,但仅逐出设置了过期时间的数据。
  • allkeys-random
    随机逐出原有数据。
  • noeviction
    不逐出任何数据,当内存已满时新数据的写入会得到一个错误信息(DEL和某些其他的命令除外)。
  • volatile-lfu
    按照LFU算法逐出原有数据,只从设置了过期时间的key中选择最不常用的key进行删除。
  • allkeys-lfu
    按照LFU算法优先逐出最不常用的key。

阿里云Redis的expire策略

针对设置了expire属性的key,Redis提供有两种过期删除策略,一种为惰性删除,一种为主动删除。惰性删除即当key过期时并不删除,每次从数据库获取该key的时候再去检查是否过期,如果过期则删除,并返回NULL。主动删除指的是通过修改hz参数的值,您可以调整Redis执行定期任务的频率,从而改变Redis清除过期key、清理超时连接等操作的效率,执行过程如下:

  1. 从设置了过期时间的key的集合中随机检查20个key。
  2. 删除检查中发现的所有过期key。
  3. 如果检查结果中25%以上的key已过期,则开始新一轮任务。

如果过期key数量很多或者增加速度很快,而Redis的主动清除频率较低,过期key将占用大量的内存空间,可能会影响Redis服务的性能。适当调整hz参数的值,提高清除频率,能够很好地解决这个问题。

相关自定义参数解读

  • hz:设置 Redis 后台任务执行频率,比如清除过期键任务,设置范围为1到500,默认为10。数值越大 CPU 消耗越大,延迟越小,建议不要超过100
  • dynamic-hz:根据客户端连接数动态调整hz的值,从而实现Redis定期任务执行频率的自动调整,Redis 5.0以后版本支持
  • azyfree-lazy-eviction:是否使用lazy free的方式驱逐key,当内存达到上限,分配失败后
  • lazyfree-lazy-expire:是否使用lazy free的方式删除设置了expire且到期的key
  • lazyfree-lazy-server-del:在隐式数据删除时,是否使用lazy free

更多关于过期key的清理策略可参考

https://help.aliyun.com/document_detail/142171.html

https://help.aliyun.com/document_detail/142379.html

https://redis.io/commands/expire

Redis内存使用最佳实践

一般来说,dataset占用了Redis实例中的绝大部分内存,所以优化dataset的大小至关重要。一般有以下的优化思路

  • 不要放垃圾数据
  • 测试数据,下线业务的数据需要及时清理
  • 给key设置合理的过期时间和清理策略
  • 对具有时效性的数据设置TTL,通过Redis自身的过期key清理机制降低过期key对内存的占用
  • 建议结合惰性删除和主动删除,惰性删除容易造成大量不被访问的过期key占用内存的问题,主动删除需要额外消耗CPU资源
  • 不同业务使用不同的逻辑db
  • 不同业务使用同一个Redis实例时,存放在不同的逻辑db上有助于业务梳理,无用业务的数据清空等
  • 过期key的主动清理会依次遍历所有db,存放不同的逻辑db更有助于提高过期数据清理效率
  • 避免出现大Key
  • Key过大会导致网络传输时延比较大,需要的输出缓冲区也更大,在定时清理过程中也容易造成比较高的延迟
  • 建议通过业务拆分,数据压缩等方式避免大Key的产生
  • 通过使用阿里云Redis控制台提供的“缓存分析”功能分析大Key,步骤如下图所示
  • 4.png
  • 配置升级
  • 主备架构的云数据库Redis最大支持64G内存,集群版本最大可支持4T内存

更多关于Redis的内存使用优化参考

https://redis.io/topics/memory-optimization





相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
4天前
|
存储 监控 NoSQL
Redis处理大量数据主要依赖于其内存存储结构、高效的数据结构和算法,以及一系列的优化策略
【5月更文挑战第15天】Redis处理大量数据依赖内存存储、高效数据结构和优化策略。选择合适的数据结构、利用批量操作减少网络开销、控制批量大小、使用Redis Cluster进行分布式存储、优化内存使用及监控调优是关键。通过这些方法,Redis能有效处理大量数据并保持高性能。
22 0
|
4天前
|
NoSQL API Redis
最佳实践|如何使用c++开发redis module
本文将试着总结Tair用c++开发redis module中遇到的一些问题并沉淀为最佳实践,希望对redis module的使用者和开发者带来一些帮助(部分最佳实践也适用于c和其他语言)。
76279 0
|
4天前
|
缓存 监控 NoSQL
Redis的主要内存淘汰策略
【5月更文挑战第15天】Redis内存淘汰策略在内存满时删除旧数据以容纳新数据。策略包括:volatile-lru/LFU/random(针对有过期时间的键),volatile-ttl(淘汰TTL最短的键),allkeys-lru/LFU(淘汰所有键),和allkeys-random。还有noeviction策略,不淘汰任何键,新写入会报错。选择策略应基于应用访问模式、数据重要性和性能需求。可以通过info命令监控缓存命中率调整策略。
14 3
|
4天前
|
存储 NoSQL 算法
Redis源码、面试指南(2)内存编码数据结构(下)
Redis源码、面试指南(2)内存编码数据结构
20 4
|
4天前
|
存储 NoSQL API
Redis源码、面试指南(2)内存编码数据结构(上)
Redis源码、面试指南(2)内存编码数据结构
14 0
|
4天前
|
NoSQL Linux Redis
Redis内存分析工具RDR
Redis内存分析工具RDR
699 1
|
4天前
|
存储 Arthas 监控
JVM工作原理与实战(三十):堆内存状况的对比分析
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了堆内存状况的对比分析、产生内存溢出的原因等内容。
15 0
|
4天前
|
缓存 Linux
linux性能分析之内存分析(free,vmstat,top,ps,pmap等工具使用介绍)
这些工具可以帮助你监视系统的内存使用情况、识别内存泄漏、找到高内存消耗的进程等。根据具体的问题和需求,你可以选择使用其中一个或多个工具来进行内存性能分析。注意,内存分析通常需要综合考虑多个指标和工具的输出,以便更好地理解系统的行为并采取相应的优化措施。
32 6
|
4天前
|
机器学习/深度学习 分布式计算 数据处理
Spark是一个基于内存的通用数据处理引擎,可以进行大规模数据处理和分析
【5月更文挑战第2天】Spark是一个基于内存的通用数据处理引擎,可以进行大规模数据处理和分析
25 3
|
4天前
|
监控 算法 测试技术
【Go语言专栏】Go语言的性能优化与内存分析
【4月更文挑战第30天】本文探讨了Go语言的性能优化策略和内存分析方法。性能优化原则包括基准测试、分析瓶颈、避免过早优化和持续监控。优化策略涉及减少内存分配、避免内存逃逸、利用并发、优化算法和数据结构以及减少系统调用。内存分析借助于Go的`pprof`工具、内存分配跟踪和第三方工具,以发现内存泄漏和管理问题。通过这些方法,开发者能提升Go程序效率和资源利用率。